/* Copyright (c) 2006, Jan Flora <janflora@diku.dk>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the University of Copenhagen nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
  @author Jan Flora <janflora@diku.dk>
*/

#include "frame.h"

module MacFrameM
{
	provides
	{
		interface MacFrame;
	}
	uses
	{		
		interface Debug;
	}

}
implementation
{
	#define DBG_LEVEL 1
	#include "Debug.h"
	
	uint8_t *updateDstAddr(macMhr_t *myFrame, uint8_t *location);
	uint8_t *updateSrcAddr(macMhr_t *myFrame, uint8_t *location);
	
	// This command requires intraPAN and address modes to be set.
	async command void MacFrame.initializeMhrStruct(macMhr_t *myFrame, uint8_t *data) __attribute__((noinline))
	{
		uint8_t *nextAddr;
		
		myFrame->frameControl = (frameControl_t*)data;
		myFrame->seqNum = data + sizeof(frameControl_t);
		nextAddr = updateDstAddr(myFrame, myFrame->seqNum+1);
		nextAddr = updateSrcAddr(myFrame, nextAddr);
		myFrame->payload = nextAddr;
	}
	
	async command void MacFrame.updateMhtStruct(macMhr_t *myFrame) __attribute__((noinline))
	{
		uint8_t *nextAddr;
		nextAddr = updateDstAddr(myFrame, myFrame->seqNum+1);
		nextAddr = updateSrcAddr(myFrame, nextAddr);
		myFrame->payload = nextAddr;
	}
	
	uint8_t *updateDstAddr(macMhr_t *myFrame, uint8_t *location)
	{
		uint8_t dstAddrMode = myFrame->frameControl->DstAddrMode;			
		if (dstAddrMode) {
			myFrame->dstPan = location;
			myFrame->dstAddr = myFrame->dstPan + 2;
			// We exploit, that 3 is the only odd mode. And that
			// the other modes are equal to their address lengths.
			return myFrame->dstAddr + (((dstAddrMode)&1)?8:(dstAddrMode));
		}
		return location;
	}
	
	uint8_t *updateSrcAddr(macMhr_t *myFrame, uint8_t *location)
	{
		uint8_t srcAddrMode = myFrame->frameControl->SrcAddrMode;
		if (srcAddrMode) {
			if (myFrame->frameControl->IntraPAN) {
				myFrame->srcPan = myFrame->dstPan;
				myFrame->srcAddr = location;
			} else {
				myFrame->srcPan = location;
				myFrame->srcAddr = myFrame->srcPan + 2;
			}
			// We exploit, that 3 is the only odd mode. And that
			// the other modes are equal to their address lengths.
			return myFrame->srcAddr + (((srcAddrMode)&1)?8:(srcAddrMode));
		}
		return location;
	}
	
	async command uint8_t MacFrame.getMhrSize(macMhr_t *myFrame) __attribute__((noinline))
	{
		uint8_t dstAddrMode = myFrame->frameControl->DstAddrMode;
		uint8_t srcAddrMode = myFrame->frameControl->SrcAddrMode;
		bool intraPAN = myFrame->frameControl->IntraPAN;
		uint8_t srcAddrSize = (((srcAddrMode)&1)?8:(srcAddrMode)) + ((!intraPAN&&srcAddrMode)?2:0);
		uint8_t dstAddrSize = (((dstAddrMode)&1)?8:(dstAddrMode)) + (dstAddrMode?2:0);
		return sizeof(frameControl_t)+1+srcAddrSize+dstAddrSize;
	}

	async command void MacFrame.intializeBeaconStruct(macBeaconPayload_t *beacon, uint8_t *payload) __attribute__((noinline))
	{
		beacon->sfSpec = (msduSuperframeSpec_t*)payload;
		beacon->gtsSpec = (msduGTSSpec_t*)((uint8_t*)(beacon->sfSpec) + sizeof(msduSuperframeSpec_t));
		beacon->gtsDirs = (msduGTSDirections_t*)((uint8_t*)(beacon->gtsSpec) + sizeof(msduGTSSpec_t));
		beacon->gtsList = (msduGTSList_t*)((uint8_t*)(beacon->gtsDirs) + sizeof(msduGTSDirections_t));
		if (beacon->gtsSpec->GTSDescriptorCount) {
			beacon->addrSpec = (msduPendingAddrSpec_t*)((uint8_t*)(beacon->gtsList) + beacon->gtsSpec->GTSDescriptorCount*sizeof(msduGTSList_t));
		} else {
			beacon->addrSpec = (msduPendingAddrSpec_t*)((uint8_t*)(beacon->gtsSpec) + sizeof(msduGTSSpec_t));
		}
		beacon->addrList = (uint8_t*)(beacon->addrSpec) + sizeof(msduPendingAddrSpec_t);
		beacon->payload = (uint8_t*)(beacon->addrList) + beacon->addrSpec->NumShortAddrsPending*2+beacon->addrSpec->NumExtAddrsPending*8;
	}

	// For use when GTS descriptor count or pending addresses changes.	
	async command void MacFrame.refreshBeaconStruct(macBeaconPayload_t *beacon) __attribute__((noinline))
	{
		if (beacon->gtsSpec->GTSDescriptorCount) {
			beacon->addrSpec = (msduPendingAddrSpec_t*)((uint8_t*)(beacon->gtsList) + beacon->gtsSpec->GTSDescriptorCount*sizeof(msduGTSList_t));
		} else {
			beacon->addrSpec = (msduPendingAddrSpec_t*)((uint8_t*)(beacon->gtsSpec) + sizeof(msduGTSSpec_t));
		}
		beacon->addrList = (uint8_t*)(beacon->addrSpec) + sizeof(msduPendingAddrSpec_t);
		beacon->payload = (uint8_t*)(beacon->addrList) + beacon->addrSpec->NumShortAddrsPending*2+beacon->addrSpec->NumExtAddrsPending*8;
	}
	
	async command uint8_t MacFrame.getBeaconSize(macBeaconPayload_t *beacon) __attribute__((noinline))
	{
		uint8_t gtsSize = beacon->gtsSpec->GTSDescriptorCount*sizeof(msduGTSList_t);
		uint8_t pendSize = sizeof(msduPendingAddrSpec_t) +
		                   beacon->addrSpec->NumShortAddrsPending * 2 +
		                   beacon->addrSpec->NumExtAddrsPending * 8;
		// If any gts's add directions overhead.
		if (gtsSize) gtsSize += sizeof(msduGTSDirections_t);
		// Calculate payload size.
		return sizeof(msduSuperframeSpec_t)+sizeof(msduGTSSpec_t)+gtsSize+pendSize;
	}
}